import {ElMessage, ElMessageBox, ElNotification} from 'element-plus'
import axios from "axios";
import useUserInfoStore from "@/stores/userInfo";
import {$config} from "@/utils/config";
import qs from 'qs'
import errorCode from "@/utils/errorCode";
import router from "@/router";
import {resetRouter} from "@/utils/utils";


const tenantEnable = true
const {resultCode, baseUrl, requestTimeout} = $config

const ignoreMsgs = [
    '无效的刷新令牌', // 刷新令牌被删除时，不用提示
    '刷新令牌已过期' // 使用刷新令牌，刷新获取新的访问令牌时，结果因为过期失败，此时需要忽略。否则，会导致继续 401，无法跳转到登出界面
]
// 是否显示重新登录
export const isRelogin = {show: false}
// 请求队列
let requestList = []
// 是否正在刷新中
let isRefreshToken = false
// 请求白名单，无须token的接口
const whiteList = ['/login', '/refresh-token']

const request = axios.create({
    baseURL: baseUrl, // api 的 base_url
    timeout: requestTimeout, // 请求超时时间
    withCredentials: false, // 禁用 Cookie 等信息
    // 自定义参数序列化函数
    paramsSerializer: (params) => {
        return qs.stringify(params, {allowDots: true})
    }
})

// request 拦截器
// 可以在请求发送前对请求做一些处理
request.interceptors.request.use(config => {
    // 设置token
    if (useUserInfoStore().ACCESS_TOKEN) {
        config.headers.Authorization = 'Bearer ' + useUserInfoStore().ACCESS_TOKEN // 让每个请求携带自定义token
    }
    // 设置租户
    if (tenantEnable) {
        const tenantId = $config.tenantId
        if (tenantId) config.headers['tenant-id'] = tenantId
    }
    const method = config.method?.toUpperCase()
    // 防止 GET 请求缓存
    if (method === 'GET') {
        config.headers['Cache-Control'] = 'no-cache'
        config.headers['Pragma'] = 'no-cache'
    }
    // 自定义参数序列化函数
    else if (method === 'POST') {
        const contentType = config.headers['Content-Type'] || config.headers['content-type']
        if (contentType === 'application/x-www-form-urlencoded') {
            if (config.data && typeof config.data !== 'string') {
                config.data = qs.stringify(config.data)
            }
        }
    }
    return config
}, error => {
    console.log(error)
    return Promise.reject(error)
});

// response 拦截器
// 可以在接口响应后统一处理结果
request.interceptors.response.use(
    async response => {
        let {data} = response
        const config = response.config
        if (!data) {
            // 返回“[HTTP]请求没有返回值”;
            throw new Error()
        }
        // 未设置状态码则默认成功状态
        // 二进制数据则直接返回，例如说 Excel 导出
        if (
            response.request.responseType === 'blob' ||
            response.request.responseType === 'arraybuffer'
        ) {
            // 注意：如果导出的响应为 json，说明可能失败了，不直接返回进行下载
            if (response.data.type !== 'application/json') {
                return response.data
            }
            data = await new Response(response.data).json()
        }
        const code = data.code || resultCode
        // 获取错误信息
        const msg = data.msg || errorCode[code] || errorCode['default']
        if (ignoreMsgs.indexOf(msg) !== -1) {
            // 如果是忽略的错误码，直接返回 msg 异常
            return Promise.reject(msg)
        } else if (code === 401) {
            // 如果未认证，并且未进行刷新令牌，说明可能是访问令牌过期了
            if (!isRefreshToken) {
                isRefreshToken = true
                // 1. 如果获取不到刷新令牌，则只能执行登出操作
                if (!useUserInfoStore().REFRESH_TOKEN) {
                    return handleAuthorized()
                }
                // 2. 进行刷新访问令牌
                try {
                    const refreshTokenRes = await refreshToken()
                    // 2.1 刷新成功，则回放队列的请求 + 当前请求
                    useUserInfoStore().setAccessToken((await refreshTokenRes).data.accessToken)
                    useUserInfoStore().setRefreshToken((await refreshTokenRes).data.refreshToken)
                    config.headers.Authorization = 'Bearer ' + useUserInfoStore().ACCESS_TOKEN
                    requestList.forEach((cb) => {
                        cb()
                    })
                    requestList = []
                    return request(config)
                } catch (e) {
                    // 2.2 刷新失败，只回放队列的请求
                    requestList.forEach((cb) => {
                        cb()
                    })
                    // 提示是否要登出。即不回放当前请求！不然会形成递归
                    return handleAuthorized()
                } finally {
                    requestList = []
                    isRefreshToken = false
                }
            } else {
                // 添加到队列，等待刷新获取到新的令牌
                return new Promise((resolve) => {
                    requestList.push(() => {
                        config.headers.Authorization = 'Bearer ' + useUserInfoStore().ACCESS_TOKEN // 让每个请求携带自定义token 请根据实际情况自行修改
                        resolve(request(config))
                    })
                })
            }
        } else if (code === 500) {
            ElMessage.error('服务器错误,请联系管理员!')
            return Promise.reject(new Error(msg))
        } else if (code === 901) {
            ElMessage.error({
                offset: 300,
                dangerouslyUseHTMLString: true,
                message:
                    '<div>' +
                    '演示模式，无法进行写操作!' +
                    '</div>'
            })
            return Promise.reject(new Error(msg))
        } else if (code !== 200) {
            if (msg === '无效的刷新令牌') {
                console.log(msg)
                return handleAuthorized()
            } else {
                ElNotification.error({title: msg})
            }
            return Promise.reject('error')
        } else {
            return data
        }
    },
    (error) => {
        console.log('err' + error)
        let {message} = error
        if (message === 'Network Error') {
            message = '操作失败,系统异常!'
        } else if (message.includes('timeout')) {
            message = '接口请求超时,请刷新页面重试!'
        } else if (message.includes('Request failed with status code')) {
            message = '请求出错，请稍候重试' + message.slice(-3)
        }
        ElMessage.error(message)
        return Promise.reject(error)
    }
)

const refreshToken = async () => {
    axios.defaults.headers.common['tenant-id'] = $config.tenantId
    axios.defaults.headers.Authorization = 'Bearer ' + useUserInfoStore().ACCESS_TOKEN
    return await axios.post(baseUrl + '/admin-api/system/auth/refresh-token?refreshToken=' + useUserInfoStore().REFRESH_TOKEN)
}

const handleAuthorized = () => {
    if (!isRelogin.show) {
        isRelogin.show = true
        ElMessageBox.confirm('登录超时,请重新登录!', '系统提示', {
            showCancelButton: false,
            closeOnClickModal: false,
            showClose: false,
            closeOnPressEscape: false,
            confirmButtonText: '重新登录',
            type: 'warning'
        }).then(() => {
            resetRouter() // 重置静态路由表
            useUserInfoStore().deleteUserCache()
            useUserInfoStore().removeToken()
            router.push('/login')
            isRelogin.show = false
        })
    }
    return Promise.reject('登录超时,请重新登录!')
}

export default request
